#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the version 3 of the GNU Lesser General Public License
#   as published by the Free Software Foundation.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Copyright (c) NEC Deutschland GmbH, NEC HPC Europe
#
# $Id$

import logging
import os
import socket

from time import sleep
from aggmon.importer import Importer, ImporterComponent
from basic_types import *


__all__ = ["SocketTxtImporter", "SocketTxtImporterComponent"]


#Constants
BACKLOG = 5
RECV_CHUNK_SIZE = 4096
BIND_LOCAL_NIC = ""
TIMEOUT = 10.0


class SocketTxtImporter(Importer):
    ''' The SocketTxtImporter opens a INET/UNIX socket, reads plain text data
    from the socket and tries to build an object out of the received data.
    Data sent to the socket must stick to the following rules.
    * each Metric must be put in one single line terminated by "\n"
    '''
    def __init__( self, port_or_path="10000", keep_alive=True, **kwds ):
        Importer.__init__( self, **kwds )
        if port_or_path.isdigit():   #parameters are ALWAYS of type string
            self.socket = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
            self.socket.bind( ( BIND_LOCAL_NIC, int( port_or_path ) ) )
        else:
            if os.path.exists( port_or_path ):
                os.unlink( port_or_path )
            self.socket = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM )
            self.socket.bind( port_or_path )
        self.socket.listen( BACKLOG )
        self.socket.settimeout( TIMEOUT )
        self.keep_alive = keep_alive
        logging.info( "listening on socket %s... " % port_or_path )

    def _recvline( self, client ):
        buffers = []
        closed = False
        while True:
            while True:
                try:
                    buf = client.recv( RECV_CHUNK_SIZE, socket.MSG_DONTWAIT )
                    if len( buf ) > 0:
                        buffers.append( buf )
                    else:   #buf == 0 and no exception: client closed connection
                        closed = True
                        break
                except IOError:               #no data to receive at the moment
                    if len( buffers ) > 0 or not self.keep_alive:
                        break
                    sleep( 1 )                #go back to receive
                except Exception:
                    raise
            bigbuffer = str( "".join( buffers ) )
            buffers = []
            for line in bigbuffer.splitlines( True ):
                if line.endswith("\n"):
                    yield line
                else:
                    buffers.append( line )
            if closed or not self.keep_alive:
                yield None

    def poll( self ):
#        while 1:
#            client, address = self.socket.accept()
        logging.debug( "SocketTxtImporter: poll()" )
        while not self._stopevent.is_set():
            logging.debug( "SocketTxtImporter: socket.accept()" )
            try:
                client, address = self.socket.accept()
            except socket.timeout:
                continue
            else:
                raise
            if address:
                client_hostname = address[0]
            else:
                client_hostname = "localhost"
            logging.info( "accepted connection from %s" % client_hostname )
            while 1:
                for line in self._recvline( client ):
                    if line == None:
                        break   #client closed connection
                    if len( line ) <= 1:
                        continue
                    try:
                        metric = eval( line )
                        if not hasattr(metric, "host"):
                            metric.host = client_hostname
                        yield metric
                    except Exception, e:
                        client.send( str( e ) + "\n" )
                if not self.keep_alive or line == None:
                    break
            client.close()
            logging.info( "closed connection to %s" % client_hostname )


class SocketTxtImporterComponent(ImporterComponent):
    def __init__( self, *args, **kwds ):
        ImporterComponent.__init__( self, *args, **kwds)
        self.allowed_kwds.extend( ["port_or_path", "keep_alive"] )

    def instantiate( self, *args, **kwds ):
        self.instance = None
        try:
            logging.debug( "instantiating: %s" % self.name )
            self.instance = SocketTxtImporter( *args, **kwds )
        except Exception, e:
            raise e
